import 'package:analyzer/dart/element/element.dart';
import 'package:build/build.dart';
import 'package:code_builder/code_builder.dart';
import 'package:fall_data_base/fall_data_base.dart';
import 'package:froom/froom.dart';
import 'package:source_gen/source_gen.dart';
import 'package:fall_gen_base/fall_gen_base.dart';

import '../utils/tc_util.dart';

/// DAO扫描代码生成器
///
/// 基于@AutoScan注解的include、exclude、annotations配置，扫描所有@Dao标注的类，
/// 生成自动注册和依赖注入逻辑作为被标注类的part文件
class DaoAutoScan extends BaseAutoScan<DaoInfo> {
  DaoAutoScan({super.debug});
  @override
  List<DaoInfo> process(
    ClassElement element,
    ConstantReader annotation,
    BuildStep buildStep,
  ) {
    print(debug);
    return [DaoInfo.fromElement(element, annotation, buildStep)];
  }

  @override
  Object genClassExtend(
      String className, List<Method> methods, List<Directive> imports) {
    return Class(
      (b) => b
        ..name = '${className}Impl'
        ..extend = refer(className)
        ..methods.addAll(methods),
    );
  }

  @override
  List<String> getAdditionalImports(
    DaoInfo service,
    String relativePath,
    Element element,
    ConstantReader annotation,
    BuildStep buildStep,
  ) {
    return [
      GenUtil.getImportPath(
          service.inputUri.replace(
              path: service.inputUri.path.replaceAll('.dart', '.g.dart')),
          buildStep.inputId.uri)
    ];
  }

  @override
  String genInjectBody(List<DaoInfo> services, Element element,
      ConstantReader annotation, BuildStep buildStep, List<Directive> imports) {
    final statements = <String>[];

    for (final service in services) {
      statements.add('// 为${service.className}注入依赖');

      // 根据服务是否有名称来决定获取方式
      if (service.serviceName != null) {
        statements.add(
          'final ${service.className.toLowerCase()}Instance = Get.find<${service.className}>(tag: "${service.serviceName}");',
        );
      } else {
        statements.add(
          'final ${service.className.toLowerCase()}Instance = Get.find<${service.className}>();',
        );
      }

      for (final field in service.injectableFields) {
        final fieldServiceName = field.serviceName;
        statements.add('// ${field.lazy ? '懒加载' : '立即'}注入${field.fieldName}');

        // 使用 InjectUtil 工具类进行注入
        final injectCode = '''
InjectUtil.inject<${field.fieldType}>(
  ${fieldServiceName != null ? '"$fieldServiceName"' : 'null'},
  (service) => ${service.className.toLowerCase()}Instance.${field.fieldName} = service,
);''';
        statements.add(injectCode);
      }
      statements.add(''); // 添加空行
    }

    return statements.join('\n');
  }

  @override
  String genRegisterBody(
    List<DaoInfo> services,
    Element element,
    ConstantReader annotation,
    BuildStep buildStep,
    List<Directive> imports,
  ) {
    final statements = <String>[];
    regDB(element, annotation, buildStep, statements, imports);
    regTypeConverter(element, annotation, buildStep, statements, imports);
    regDao(services, statements);

    return statements.join('\n');
  }

  void regDao(List<DaoInfo> services, List<String> statements) {
    for (final service in services) {
      final className = service.className;
      final serviceName = service.serviceName;

      statements.add('// 注册${service.serviceName ?? service.className}');

      if (serviceName != null) {
        statements.add(
          'Get.put<${className}>(${className}Impl(db,listener), tag: "$serviceName");',
        );
      } else {
        statements.add('Get.put<${className}>(${className}Impl(db,listener));');
      }
    }
  }

  void regTypeConverter(Element element, ConstantReader annotation,
      BuildStep buildStep, List<String> statements, List<Directive> imports) {
    final tcAnno = AnnoUtil.getAnnotation(element, TypeConverters);
    final tcs = AnnoUtil.readListTypeElemet(ConstantReader(tcAnno), 'value');
    for (final tc in tcs) {
      final libraryUrl = tc.library?.identifier;
      final typeName = tc.name;
      LogUtil.d('reg TypeConverter: $typeName from $libraryUrl');
      statements.add('Get.put<${tc.name}>(${tc.name}());');
    }
  }

  void regDB(Element element, ConstantReader annotation, BuildStep buildStep,
      List<String> statements, List<Directive> imports) {
    if (element is! ClassElement) return;
    imports.add(Directive.import('dart:async'));
    imports.add(Directive.import('package:froom/froom.dart'));

    imports
        .add(Directive.import('package:sqflite/sqflite.dart', as: 'sqflite'));
    imports.add(Directive.import('package:fall_data_base/fall_data_base.dart'));
    imports.add(Directive.import('package:fall_data_main/fall_data_main.dart'));
    final dbNameValue = AnnoUtil.getAnnotationValue(element, DB, 'name', null);
    statements.add('// 注册数据库');
    statements.add('final listener = StreamController<String>.broadcast();');
    statements.add('final db=await DbUtil.openDb(name,migrations,callback);');

    // 注册数据库到容器
    if (dbNameValue != null) {
      statements.add('Get.put<sqflite.Database>(db, tag: "$dbNameValue");');
    } else {
      statements.add('Get.put<sqflite.Database>(db);');
    }

    statements.add('');
  }
}

/// DAO信息
class DaoInfo implements ServiceInfoBase {
  @override
  final String className;
  @override
  final Uri inputUri;
  @override
  final String? serviceName;
  final String? dbName;
  @override
  final List<InjectableField> injectableFields;

  DaoInfo({
    required this.className,
    required this.inputUri,
    this.serviceName,
    this.dbName,
    required this.injectableFields,
  });

  factory DaoInfo.fromElement(
    ClassElement element,
    ConstantReader annotation,
    BuildStep buildStep,
  ) {
    // 读取@Service注解参数
    final serviceName = annotation.read('name').isNull
        ? null
        : annotation.read('name').stringValue;
    String dbName = TcUtil.getDbName(element);
    // 收集@Auto标注的字段
    final injectableFields = InjectableField.collectInjectableFields(element);

    return DaoInfo(
      className: element.name ?? 'UnknownClass',
      inputUri: element.library.uri,
      serviceName: serviceName,
      dbName: dbName,
      injectableFields: injectableFields,
    );
  }
}
